package models.re.simulatedData;

import java.util.ArrayList;
import java.util.Random;

import javax.persistence.Query;

import models.re.IData;
import models.re.IItem;
import models.re.IRank;
import models.re.IRelation;
import models.re.ISimilarity;

import play.Logger;
import play.db.jpa.JPA;


public class SimulatedDataOne implements IData {
	
	private static ArrayList<Long> assetIds = new ArrayList<Long>();
	private static ArrayList<Long> spaceIds = new ArrayList<Long>();
	private static ArrayList<Long> actorIds = new ArrayList<Long>();
	
	public static void GenerateDatas() {
		GenerateDatas(100);
	}
	
	public static void GenerateDatas(int dim) {
		GenerateDatas(dim,dim);
	}
	
	public static void GenerateDatas(int nodesDim, int relationsDim) {		
		SimulatedDataOne sdo = new SimulatedDataOne();
    	sdo.cleanAllTables();
    //create actor, asset and space from 3 different platforms. each entity has its unique id.
    	CreateNodes(nodesDim);
    //create relations, including fillin similar table
    	CreateRelations(relationsDim);
    	Logger.info("Nodes: "+sdo.getAllNodesIds().size()+" - Edges: "+(sdo.getAllSimilarities().size()+
    			sdo.findAllInRelation().size()));
	}
	
	//generate simulated data
	//dataset: 1 graasp, 2 UniGe Moodle, 3 Fribourg Moodle
	
	@SuppressWarnings("unchecked")
	private ArrayList<Relation> findAllInRelation() {
		// TODO Auto-generated method stub
		String sql = "SELECT e FROM Relation e";
        Query query = JPA.em().createQuery(sql);
        return new ArrayList<Relation>(query.getResultList());
	}

	public static void CreateNodes(int dim){		
		SimulatedDataOne sdo = new SimulatedDataOne();
		// DN is the number of datasets, should be public 
		final int DN = 3;
    	//add actors: graasp: 300, UniGe: 300, Fribourg: 300
		for(int dataset = 1; dataset<=
				DN; dataset++)
		{
			String db_id = Integer.toString(dataset);
			int actor_number = 0;
			switch(dataset)
			{
			case 1: 
				actor_number = 3*dim;
				break;
			case 2:
				actor_number =3*dim;
				break;
			case 3:
				actor_number =3*dim;
				break;
			default:
				System.out.println("dataset number is wrong!");
				break;
			}
			for(int i=0;i<actor_number;i++)
				{
				actorIds.add(sdo.Add("actor", db_id, "actor"+(dataset*10000+i)));
				}
		}
		// add asset
		for(int dataset = 1; dataset<=
				DN; dataset++)
		{
			String db_id = Integer.toString(dataset);
			int asset_number = 0;
			switch(dataset)
			{
			case 1: 
				asset_number = 12*dim;
				break;
			case 2:
				asset_number =8*dim;
				break;
			case 3:
				asset_number =6*dim;
				break;
			default:
				System.out.println("dataset number is wrong!");
				break;
			}
			for(int i=0;i<asset_number;i++)
				{
				assetIds.add(sdo.Add("asset", db_id, "asset"+(dataset*10000+i)));
				}
		}
		//add spaces
		for(int dataset = 1; dataset<=
				DN; dataset++)
		{
			String db_id = Integer.toString(dataset);
			int space_number = 0;
			switch(dataset)
			{
			case 1: 
				space_number = 3*dim;
				break;
			case 2:
				space_number =6*dim;
				break;
			case 3:
				space_number =5*dim;
				break;
			default:
				System.out.println("dataset number is wrong!");
				break;
			}
			for(int i=0;i<space_number;i++)
				{
				spaceIds.add(sdo.Add("space", db_id, "space"+(dataset*10000+i)));
				}
		}
    	
	}
	//create relations
	//create belong: most assets belong to some space, some spaces belong to actors, change belong to has:
	//source A has target B
	public static void CreateRelations(int dim){
		SimulatedDataOne sdo = new SimulatedDataOne();
		// DN is the number of datasets, should be public 
    	Random rd = new Random();
    	//create space has assets (all within a certain platform)
    	//rd_scale is to set the possibility of connecting two entities.
    	int rd_scale = 10;
    	ArrayList<IItem> items = null;
    	for(IItem space: sdo.findAllCertainNodes("space")) {
    		if(items==null) items = sdo.findAllCertainNodes("asset");
    		for(IItem asset: items)
    		{
    			//create belong in 1st platform
    			if(space.getDataset().equals(asset.getDataset())){
    				if(space.getDataset().equals("1"))
    				{
    					rd_scale = 4;
    					if(rd.nextInt(rd_scale-1)==0){
    						sdo.CreateRelation(space.getId(), asset.getId(), "belong");
    					}
    				
    				}
    			}
    			//create belong in 2nd platform
    			if(space.getDataset().equals(asset.getDataset())){
    				if(space.getDataset().equals("2"))
    				{
    					rd_scale = 3;
    					if(rd.nextInt(rd_scale-1)==0){
    						sdo.CreateRelation(space.getId(), asset.getId(), "belong");
    					}
    				
    				}
    			}
    			//create belong in 3rd platform
    			if(space.getDataset().equals(asset.getDataset())){
    				if(space.getDataset().equals("3"))
    				{
    					rd_scale = 2;
    					if(rd.nextInt(rd_scale-1)==0){
    						sdo.CreateRelation(space.getId(), asset.getId(), "belong");
    					}
    				
    				}
    			}
    		}
    		
    	}
    	rd_scale = 100;
    	items = null;
    	for(IItem space: sdo.findAllCertainNodes("space")) {
    		if(items==null) items = sdo.findAllCertainNodes("actor");
    		for(IItem actor: items) {
    			if(space.getDataset().equals(actor.getDataset())){
    				if(rd.nextInt(rd_scale)==1){
    					sdo.CreateRelation(actor.getId(), space.getId(), "create");
    					break;
    				}
    			}
    		}
    	}
    
 
    	//create interaction relations. Actor can give actions to items from all platforms.		
    	for(int i=0;i<35*dim;i++) sdo.CreateRelation(actorIds.get(rd.nextInt(actorIds.size())), 
    			assetIds.get(rd.nextInt(assetIds.size())), "visite");
    	for(int i=0;i<10*dim;i++) sdo.CreateRelation(actorIds.get(rd.nextInt(actorIds.size())), 
    			assetIds.get(rd.nextInt(assetIds.size())), "like");
    	for(int i=0;i<10*dim;i++) sdo.CreateRelation(actorIds.get(rd.nextInt(actorIds.size())), 
    			assetIds.get(rd.nextInt(assetIds.size())), "favorite");
    	for(int i=0;i<10*dim;i++) sdo.CreateRelation(actorIds.get(rd.nextInt(actorIds.size())), 
    			assetIds.get(rd.nextInt(assetIds.size())), "ignore");
    	
    	
    	 /*create similarity
    	 * for asset in assets //all entities, including space, actor and assets
    	 * randomly assign 100 other entities as similar entity with the asset
    	 * e.g: for(i=1..100) create(asset.id, random(asset.id), "similar", similar_value);
    	 *                                     ---should not be same with asset.id  ----similar_value is a random value between 0.5 to 1
    	 */
    	ArrayList<Long> allItems = new ArrayList<Long>(assetIds);
    	allItems.addAll(actorIds);
    	allItems.addAll(spaceIds);
    	for(Long item: allItems) {
    		for(int i=0;i<1*dim;i++) {    		
    			Long target = item;
    			while(item==target) target = allItems.get(rd.nextInt(allItems.size()));
    			sdo.CreateSimilarity(item, target, 0.5+rd.nextDouble()/2.0);
    		}
    	}
    
    	
		
	}
	
	@SuppressWarnings("unchecked")
	public ArrayList<String> getAllNodesIds() {
		String sql = "SELECT e.id FROM Item e";
        Query query = JPA.em().createQuery(sql);
        ArrayList<Long> tmp = new ArrayList<Long>(query.getResultList());
        ArrayList<String> result = new ArrayList<String>();
        for(Long val:tmp) result.add(Long.toString(val));
        return result;
	}

	public Long Add(String type, String dataset, String name) {
		// TODO Auto-generated method stub
		Item item = new Item(type,dataset,name);
		item.save();
		return item.getId();
	}

	public void Remove(Long id) {		
		// TODO Auto-generated method stub
		Item.get(id).delete();
	}

	public IItem Find(Long id) {
		// TODO Auto-generated method stub
		return Item.get(id);
	}
	
	/* (non-Javadoc)
	 * @see models.re.simulatedData.Data#findAllInRelation(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public ArrayList<IRelation> findAllInRelation(String type) {
		String sql = "SELECT e FROM Relation e WHERE e.type = ? ";
        Query query = JPA.em().createQuery(sql);
        query.setParameter(1, Relation.str2Relation(type));
        ArrayList<Relation> tmp = new ArrayList<Relation>(query.getResultList());
        ArrayList<IRelation> res = new ArrayList<IRelation>();
        for(IRelation s: tmp) res.add(s);
        return res;
	}
	
	/* (non-Javadoc)
	 * @see models.re.simulatedData.Data#findAllCertainNodes(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public ArrayList<IItem> findAllCertainNodes(String type) {
		String sql = "SELECT e FROM Item e WHERE e.type = ? ";
        Query query = JPA.em().createQuery(sql);
        query.setParameter(1, Item.str2Item(type));
        ArrayList<Item> tmp = new ArrayList<Item>(query.getResultList());
        ArrayList<IItem> res = new ArrayList<IItem>();
        for(IItem s: tmp) res.add(s);
        return res;
	}

	/* (non-Javadoc)
	 * @see models.re.simulatedData.Data#CreateRelation(java.lang.Long, java.lang.Long, java.lang.String)
	 */
	public void CreateRelation(Long source, Long target, String type) {
		// TODO Auto-generated method stub
		Relation rel = new Relation(Item.get(source),Item.get(target),type);
		rel.save();
	}
	
	public void cleanAllTables() {
		String sql = "DELETE FROM Similarity";
        Query query = JPA.em().createQuery(sql);
        query.executeUpdate();
        sql = "DELETE FROM Rank";
        query = JPA.em().createQuery(sql);
        query.executeUpdate();
        sql = "DELETE FROM Relation";
        query = JPA.em().createQuery(sql);
        query.executeUpdate();
        sql = "DELETE FROM Item";
        query = JPA.em().createQuery(sql);
        query.executeUpdate();
        assetIds.clear();
        actorIds.clear();
        spaceIds.clear();
	}

	/* (non-Javadoc)
	 * @see models.re.simulatedData.Data#Similarity(java.lang.Long, int)
	 */
	@SuppressWarnings("unchecked")
	public ArrayList<ISimilarity> Similarity(Long sourceId, int limit) {
		// TODO Auto-generated method stub
		String sql = "SELECT e FROM Similarity e WHERE e.source = ? LIMIT = ? ORDER BY e.value";
        Query query = JPA.em().createQuery(sql);
        query.setParameter(1, sourceId);
        query.setParameter(2, limit);
        ArrayList<Similarity> tmp = new ArrayList<Similarity>(query.getResultList().subList(0,limit));
        ArrayList<ISimilarity> res = new ArrayList<ISimilarity>();
        for(ISimilarity s: tmp) res.add(s);
        return res;
	}

	/* (non-Javadoc)
	 * @see models.re.simulatedData.Data#Rank(java.lang.Long, int)
	 */
	@SuppressWarnings("unchecked")
	public ArrayList<IRank> Rank(Long actorId, int limit) {
		// TODO Auto-generated method stub
		String sql = "SELECT e FROM Rank e WHERE e.source = ? LIMIT = ? ORDER BY e.value";
        Query query = JPA.em().createQuery(sql);
        query.setParameter(1, actorId);
        query.setParameter(2, limit);
        ArrayList<Rank> tmp = new ArrayList<Rank>(query.getResultList().subList(0,limit));
        ArrayList<IRank> res = new ArrayList<IRank>();
        for(IRank s: tmp) res.add(s);
        return res;
	}

	/* (non-Javadoc)
	 * @see models.re.simulatedData.Data#CreateSimilarity(java.lang.Long, java.lang.Long, java.lang.Double)
	 */
	public void CreateSimilarity(Long source, Long target, Double value) {
		// TODO Auto-generated method stub
		Similarity sim = new Similarity(Item.get(source),Item.get(target),value);
		sim.save();
	}

	/* (non-Javadoc)
	 * @see models.re.simulatedData.Data#CReateRank(java.lang.Long, java.lang.Long, java.lang.Double)
	 */
	public void CReateRank(Long source, Long target, Double value) {
		// TODO Auto-generated method stub
		IItem actor = Item.get(source);
		try {
			if(actor.getType()!=models.aaa.Asset.Type.ACTOR) throw new Exception();
			Rank rank = new Rank(actor,Item.get(target),value);
			rank.save();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public ArrayList<models.re.ISimilarity> getAllSimilarities() {
		// TODO Auto-generated method stub
		String sql = "SELECT e FROM Similarity e";
        Query query = JPA.em().createQuery(sql);
        ArrayList<Similarity> tmp = new ArrayList<Similarity>(query.getResultList());
        ArrayList<ISimilarity> res = new ArrayList<ISimilarity>();
        for(ISimilarity s: tmp) res.add(s);
        return res;
	}

	@Override
	public ArrayList<IRelation> findAllInRelation(String type, String id) {
		// TODO Auto-generated method stub
		return null;
	}

}
